package com.ruoyi.system.service.impl;

import cn.dev33.satoken.secure.BCrypt;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.common.constant.UserConstants;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.common.core.domain.entity.SysDept;
import com.ruoyi.common.core.domain.entity.SysRole;
import com.ruoyi.common.core.domain.entity.SysTenant;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.enums.PostCodeEnum;
import com.ruoyi.common.enums.RoleCodeEnum;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.BeanCopyUtils;
import com.ruoyi.common.utils.CollectionUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.TenantUtils;
import com.ruoyi.system.domain.SysPost;
import com.ruoyi.system.domain.bo.SysTenantBo;
import com.ruoyi.system.domain.vo.SysTenantPackageVo;
import com.ruoyi.system.domain.vo.SysTenantVo;
import com.ruoyi.system.mapper.SysTenantMapper;
import com.ruoyi.system.service.*;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 租户Service业务层处理
 *
 * @author ace
 * @date 2022-10-10
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class SysTenantServiceImpl implements ISysTenantService {

    private final SysTenantMapper baseMapper;

    private final ISysTenantPackageService tenantPackageService;

    private final ISysRoleService roleService;

    private final ISysUserService userService;

    private final ISysPostService postService;

    private final ISysDeptService deptService;

    /**
     * 查询租户
     */
    @Override
    public SysTenantVo queryById(Long tenantId){
        return baseMapper.selectVoById(tenantId);
    }

    /**
     * 查询租户列表
     */
    @Override
    public TableDataInfo<SysTenantVo> queryPageList(SysTenantBo bo, PageQuery pageQuery) {
        QueryWrapper<SysTenant> lqw = getQueryWrapper(bo);
        Page<SysTenant> tenantList = baseMapper.selectTenantList(pageQuery.build(), lqw);
        List<SysTenantVo> sysTenantVos = BeanCopyUtils.copyList(tenantList.getRecords(), SysTenantVo.class);
        return new TableDataInfo<>(sysTenantVos, Objects.requireNonNull(sysTenantVos).size());
    }

    private QueryWrapper<SysTenant> getQueryWrapper(SysTenantBo bo) {
        QueryWrapper<SysTenant> wrapper = Wrappers.query();
        wrapper.like(StringUtils.isNotBlank(bo.getTenantName()), "st.tenant_name", bo.getTenantName());
        wrapper.eq(StringUtils.isNotBlank(bo.getStatus()), "st.status", bo.getStatus());
        return wrapper;
    }

    /**
     * 查询租户列表
     */
    @Override
    public List<SysTenantVo> queryList(SysTenantBo bo) {
        LambdaQueryWrapper<SysTenant> lqw = buildQueryWrapper(bo);
        return baseMapper.selectVoList(lqw);
    }

    private LambdaQueryWrapper<SysTenant> buildQueryWrapper(SysTenantBo bo) {
        Map<String, Object> params = bo.getParams();
        LambdaQueryWrapper<SysTenant> lqw = Wrappers.lambdaQuery();
        lqw.like(StringUtils.isNotBlank(bo.getTenantName()), SysTenant::getTenantName, bo.getTenantName());
        lqw.eq(StringUtils.isNotBlank(bo.getStatus()), SysTenant::getStatus, bo.getStatus());
        return lqw;
    }

    /**
     * 新增租户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean insertByBo(SysTenantBo bo) {
        SysTenant add = BeanUtil.toBean(bo, SysTenant.class);
        // 校验套餐是否存在
        SysTenantPackageVo tenantPackage = checkUpdateTenantPackage(bo.getPackageId());
        SysUser sysUser = new SysUser();
        sysUser.setUserName(bo.getUserName());
        if (UserConstants.NOT_UNIQUE.equals(userService.checkUserNameUnique(sysUser))) {
            throw  new ServiceException("新增用户'" + bo.getUserName() + "'失败，登录账号已存在");
        }
        validEntityBeforeSave(add);
        boolean flag = baseMapper.insert(add) > 0;
        if (flag) {
            bo.setTenantId(add.getTenantId());
            TenantUtils.execute(add.getTenantId(), () -> {
                // 创建角色
                Long roleId = createRole(tenantPackage);
                // 创建岗位
                Long postId = createPost();
                // 创建部门信息
                Long deptId = createDept();
                // 创建用户，并分配角色
                Long userId = createUser(roleId, postId, deptId, bo);
                SysTenant sysTenant = new SysTenant();
                sysTenant.setTenantId(add.getTenantId());
                sysTenant.setUserId(userId);
                // 修改租户的管理员
                baseMapper.updateById(sysTenant);
            });
        }
        return flag;
    }

    /**
     * 创建部门
     * @return 部门ID
     */
    private Long createDept() {
        SysDept dept = SysDept.getTenantDept();
        deptService.insertDept(dept);
        return dept.getDeptId();
    }

    /**
     * 创建用户并分配角色
     * @param roleId 角色ID
     * @param postId 岗位ID
     * @param deptId 部门ID
     * @param bo 租户bo
     * @return 用户ID
     */
    private Long createUser(Long roleId, Long postId, Long deptId, SysTenantBo bo) {
        SysUser user = new SysUser(roleId,postId,deptId,bo.getUserName(), BCrypt.hashpw(bo.getPassword()));
        // 创建用户
        userService.insertUser(user);
        return user.getUserId();
    }

    /**
     * 创建角色
     * @param tenantPackage 租户套餐
     * @return 角色ID
     */
    private Long createRole(SysTenantPackageVo tenantPackage) {
        // 创建角色
        SysRole role = new SysRole(RoleCodeEnum.TENANT_ADMIN.getName(),RoleCodeEnum.TENANT_ADMIN.getCode(),0,"系统自动生成","1");
        Long[] ids = Arrays.stream(tenantPackage.getMenuIds().split(",")).map(Long::valueOf).collect(Collectors.toList()).toArray(new Long[]{});
        role.setMenuIds(ids);
        roleService.insertRole(role);
        return role.getRoleId();
    }

    /**
     * 创建岗位
     * @return 岗位ID
     */
    private Long createPost(){
        SysPost sysPost = new SysPost(PostCodeEnum.CEO.getName(),PostCodeEnum.CEO.getCode(),0,"系统自动生成");
        postService.insertPost(sysPost);
        return sysPost.getPostId();
    }

    /**
     * 修改租户
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Boolean updateByBo(SysTenantBo bo) {
        SysTenant update = BeanUtil.toBean(bo, SysTenant.class);
        // 校验存在
        SysTenant tenant = checkUpdateTenant(update.getTenantId());
        // 校验套餐是否存在
        SysTenantPackageVo tenantPackage = checkUpdateTenantPackage(bo.getPackageId());
        validEntityBeforeSave(update);
        boolean flag = baseMapper.updateById(update) > 0;
        if(flag){
            // 如果套餐发生变化，则修改其角色的权限
            if (ObjectUtil.notEqual(tenant.getPackageId(), update.getPackageId())) {
                Set<Long> menuIds = Arrays.stream(tenantPackage.getMenuIds().split(",")).map(Long::valueOf).collect(Collectors.toSet());
                updateTenantRoleMenu(tenant.getTenantId(), menuIds);
            }
        }
        return flag;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateTenantRoleMenu(Long tenantId, Set<Long> menuIds) {
        TenantUtils.execute(tenantId, () -> {
            // 获得所有角色
            List<SysRole> roles = roleService.selectRoleAll();
            roles.forEach(role -> Assert.isTrue(tenantId.equals(role.getTenantId()), "角色({}/{}) 租户不匹配",
                    role.getRoleId(), role.getTenantId(), tenantId)); // 兜底校验
            // 重新分配每个角色的权限
            roles.forEach(role -> {
                // 如果是租户管理员，重新分配其权限为租户套餐的权限
                if (Objects.equals(role.getRoleKey(), RoleCodeEnum.TENANT_ADMIN.getCode())) {
                    roleService.assignRoleMenu(role.getRoleId(), menuIds);
                    log.info("[updateTenantRoleMenu][租户管理员({}/{}) 的权限修改为({})]", role.getRoleId(), role.getTenantId(), menuIds);
                    return;
                }
                // 如果是其他角色，则去掉超过套餐的权限
                Set<Long> roleMenuIds = roleService.getMenuPermission(role.getRoleId());
                roleMenuIds = CollUtil.intersectionDistinct(roleMenuIds, menuIds);
                roleService.assignRoleMenu(role.getRoleId(), roleMenuIds);
                log.info("[updateTenantRoleMenu][角色({}/{}) 的权限修改为({})]", role.getRoleId(), role.getTenantId(), roleMenuIds);
            });
        });
    }


    /**
     * 租户套餐校验
     * @return 对象
     */
    private SysTenantPackageVo checkUpdateTenantPackage(Long tenantPackageId){
        SysTenantPackageVo tenantPackage = tenantPackageService.queryById(tenantPackageId);
        if(Objects.isNull(tenantPackage)){
            throw new ServiceException("该租户套餐不存在");
        }
        return tenantPackage;
    }

    /**
     * 租户操作校验
     * @return 对象
     */
    private SysTenant checkUpdateTenant(Long tenantId) {
        SysTenant tenant = baseMapper.selectById(tenantId);
        if (tenant == null) {
            throw new ServiceException("该租户不存在");
        }
        // 内置租户，不允许操作
        if (tenant.isAdminTenant()) {
            throw new ServiceException("超级租户，不允许操作");
        }
        return tenant;
    }

    /**
     * 保存前的数据校验
     */
    private void validEntityBeforeSave(SysTenant entity){
        //TODO 做一些数据校验,如唯一约束

    }

    /**
     * 批量删除租户
     */
    @Override
    public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
        if(isValid){
            //TODO 做一些业务上的校验,判断是否需要校验
        }
        return baseMapper.deleteBatchIds(ids) > 0;
    }

    /**
     * 获取所有租户ID
     *
     * @return 租户编号数组
     */
    @Override
    public List<Long> getTenantIds() {
        List<SysTenant> tenants = baseMapper.selectList();
        return CollectionUtils.convertList(tenants, SysTenant::getTenantId);
    }

    /**
     * 校验租户是否合法
     *
     * @param id 租户编号
     */
    @Override
    public void validTenant(Long id) {
        SysTenant tenant = baseMapper.selectById(id);
        if (tenant == null) {
            throw new ServiceException("租户不存在");
        }
        if (tenant.getStatus().equals(SysTenant.DISABLE)) {
            throw new ServiceException("名字为【"+tenant.getTenantName()+"】的租户已被禁用");
        }
    }

    /**
     * 批量修改租户状态
     *
     * @param tenantIds 主键
     * @param type      状态
     * @return 结果
     */
    @Override
    public Boolean changeStatus(String tenantIds, String type) {
        if(!(type.equals(SysTenant.ENABLE) || type.equals(SysTenant.DISABLE))){
            throw new ServiceException("非法传递租户状态");
        }
        List<String> idList = Arrays.asList(tenantIds.split(","));
        idList.forEach(e ->{
            if(e.equals(String.valueOf(UserConstants.ADMIN_TENANT_ID))){
                throw new ServiceException("不允许操作系统租户");
            }
        });
        List<SysTenant> tenantList = idList.stream().map(e -> {
            return new SysTenant(Long.valueOf(e), type);
        }).collect(Collectors.toList());
        return baseMapper.updateBatchById(tenantList);
    }

    /**
     * 获得使用指定套餐的租户数量
     *
     * @param packageId 租户套餐编号
     * @return 租户数量
     */
    @Override
    public Long getTenantCountByPackageId(Long packageId) {
        return baseMapper.selectCount(new LambdaQueryWrapper<SysTenant>().eq(SysTenant::getPackageId,packageId));
    }

    /**
     * 获得使用指定套餐的租户数组
     *
     * @param packageId 租户套餐编号
     * @return 租户数组
     */
    @Override
    public List<SysTenant> getTenantListByPackageId(Long packageId) {
        return baseMapper.selectList(new LambdaQueryWrapper<SysTenant>().eq(SysTenant::getPackageId,packageId));
    }
}
